/*********************************************************
 * File Name   : v4l2-demo.c
 * Author      : Elvis Wang
 * Mail        : huan.wang@ingenic.com
 * Created Time: 2019-12-12 15:37
 ********************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/videodev2.h>

struct buffer {
	void *start;
	unsigned int length;
};
struct buffer buffers[4];

int main()
{
	int fd, ret;
	fd = open("/dev/video0", O_RDWR/* | O_NONBLOCK*/, 0);

	struct v4l2_capability cap;
	ret = ioctl (fd, VIDIOC_QUERYCAP, &cap);
	printf("QUERYCAP:\n");
	printf("\tdriver: %s\n", cap.driver);
	printf("\tcard: %s\n", cap.card);
	printf("\tbus_info: %s\n", cap.bus_info);
	printf("\tversion: 0x%08x\n", cap.version);
	printf("\tcapabilities: 0x%08x\n", cap.capabilities);
	printf("\treserved: 0x%08x\n", cap.reserved);

	struct v4l2_fmtdesc fmtdesc;
	fmtdesc.index = 0;
	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
	printf("ENUM_FMT:\n");
	printf("\tindex: %d\n", fmtdesc.index);
	switch(fmtdesc.type) {
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
			printf("\ttype: V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); break;
		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
			printf("\ttype: V4L2_BUF_TYPE_VIDEO_OUTPUT\n"); break;
		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
			printf("\ttype: V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); break;
		case V4L2_BUF_TYPE_VBI_CAPTURE:
			printf("\ttype: V4L2_BUF_TYPE_VBI_CAPTURE\n"); break;
		case V4L2_BUF_TYPE_VBI_OUTPUT:
			printf("\ttype: V4L2_BUF_TYPE_VBI_OUTPUT\n"); break;
		case V4L2_BUF_TYPE_PRIVATE:
			printf("\ttype: V4L2_BUF_TYPE_PRIVATE\n"); break;
		default:
			printf("\ttype: 0x%x\n", fmtdesc.type);
	}
	printf("\tflags: 0x%08x\n", fmtdesc.flags);
	printf("\tdescription: %s\n", fmtdesc.description);
	printf("\tpixelformat: 0x%08x\n", fmtdesc.pixelformat);

	struct v4l2_format format;
	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	format.fmt.pix.width = 1920;
	format.fmt.pix.height= 1080;
	format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	ret = ioctl(fd, VIDIOC_TRY_FMT, &format);

	memset(&format, 0x0, sizeof(format));
	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	format.fmt.pix.width = 1920;
	format.fmt.pix.height= 1080;
	format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	ret = ioctl(fd, VIDIOC_S_FMT, &format);

	memset(&format, 0x0, sizeof(format));
	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_G_FMT, &format);
	printf("FORMAT:\n");
	switch(format.type) {
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
			printf("\ttype: V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); break;
		case V4L2_BUF_TYPE_PRIVATE:
			printf("\ttype: V4L2_BUF_TYPE_PRIVATE\n"); break;
		default:
			printf("\ttype: 0x%x\n", format.type);
	}
	printf("\twidth: %d\n", format.fmt.pix.width);
	printf("\theight: %d\n", format.fmt.pix.height);
	printf("\tpixelformat: 0x%08x\n", format.fmt.pix.pixelformat);
	printf("\tfield: 0x%08x\n", format.fmt.pix.field);
	printf("\tbytesperline: %d\n", format.fmt.pix.bytesperline);
	printf("\tsizeimage: %d\n", format.fmt.pix.sizeimage);
	printf("\tcolorspace: 0x%08x\n", format.fmt.pix.colorspace);

	struct v4l2_requestbuffers reqbuf;
	reqbuf.count  = 4;
	reqbuf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuf.memory = V4L2_MEMORY_MMAP;
	ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf);
	sleep(1);
	printf("REQUESTBUFFERS:\n");
	printf("\treqbuf.count: %d\n", reqbuf.count);
	printf("\treqbuf.type: %d\n", reqbuf.type);

	struct v4l2_buffer vbuf;
	printf("V4L2_BUF:\n");
	int buf_cnt = 0;
	for (buf_cnt = 0; buf_cnt < reqbuf.count; buf_cnt++) {
		vbuf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuf.memory = V4L2_MEMORY_MMAP;
		vbuf.index = buf_cnt;
		ret = ioctl(fd, VIDIOC_QUERYBUF, &vbuf);

		printf("\tBUF[%d]:\n", buf_cnt);
		printf("\t\tindex: %d\n", vbuf.index);
		printf("\t\ttype: %d\n", vbuf.type);
		printf("\t\tbyteused: %d\n", vbuf.bytesused);
		printf("\t\tflags: %x\n", vbuf.flags);
		printf("\t\tfield: %d\n", vbuf.field);
		printf("\t\tsequence: %d\n", vbuf.sequence);
		printf("\t\tmemory: %d\n", vbuf.memory);
		printf("\t\toffset: %d\n", vbuf.m.offset);
		printf("\t\tlength: %d\n", vbuf.length);
		/*printf("\t\tinput: %d\n", vbuf.input);*/

		buffers[buf_cnt].length = vbuf.length;
		buffers[buf_cnt].start  = mmap(NULL, vbuf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vbuf.m.offset);
	}

	buf_cnt = 0;
	for (buf_cnt = 0; buf_cnt < reqbuf.count; buf_cnt++) {
		memset(&vbuf, 0x0, sizeof(struct v4l2_buffer));
		vbuf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuf.memory = V4L2_MEMORY_MMAP;
		vbuf.index = buf_cnt;
		ret = ioctl(fd, VIDIOC_QBUF, &vbuf);
		if (ret) {
			printf("VIDIOC_QBUF %d error, %s\n", buf_cnt, strerror(errno));
		}
	}

	enum v4l2_buf_type type;
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_STREAMON, &type);
	if (ret) {
		printf("VIDIOC_STREAMON %d error, %s\n", buf_cnt, strerror(errno));
	}

	int k = 0;
	while(1) {
		memset(&vbuf, 0x0, sizeof(struct v4l2_buffer));
		vbuf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		vbuf.memory = V4L2_MEMORY_MMAP;
		vbuf.index = k;
		ret = ioctl(fd, VIDIOC_DQBUF, &vbuf);
		if (ret) {
			printf("VIDIOC_DQBUF %d error, %s\n", k, strerror(errno));
		}

		ret = ioctl(fd, VIDIOC_QBUF, &vbuf);
		if (ret) {
			printf("VIDIOC_QBUF %d error, %s\n", k, strerror(errno));
		}

		k++;
		if (k == 4)
			k = 0;
	}

	close(fd);

	return 0;
}
